#include "datacenter.h"

#include<QStandardPaths>
#include<QJsonObject>
#include<QJsonDocument>
#include<QFile>
#include<QDir>

namespace model {
DataCenter* DataCenter::instance = nullptr;

DataCenter *DataCenter::getInstance()
{
    if(instance == nullptr){
        instance = new DataCenter();
    }
    return instance;
}

DataCenter::~DataCenter()
{
    //释放所有成员
    delete myself;
    delete friendList;
    delete chatSessionList;
    delete memberList;
    delete applyList;
    delete recentMessages;
    delete unreadMessageCount;
    delete searchUserResult;
    delete searchMessageResult;
}

DataCenter::DataCenter():netClient(this)
{
    //主要是使用nullptr表示"非法状态"
    //对于hash来说，不关心整个QHash是否nullptr
    //通过key是否存在，也能表示value是否有效
    recentMessages = new QHash<QString,QList<Message>>;
    memberList = new QHash<QString,QList<UserInfo>>;
    unreadMessageCount = new QHash<QString,int>;
}

void DataCenter::initDataFile()
{
    //构造出文件的路径，使用appData存储文件
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + "/ChatClient.json";
    LOG() << "filePath" << filePath;

    //构造好文件路径之后，把文件创建出来
    //写方式打开，并写入初始内容
    QFile file(filePath);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        LOG()<<"打开文件失败！";
        return;
    }
    //打开成功，写入初始内容
    QString data = "{\n\n}";
    file.write(data.toUtf8());
    file.close();
}

void DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    QFile file(filePath);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        LOG()<<"打开文件失败！";
        return;
    }

    //LOG() << file.errorString();

    if(!file.exists()){
        LOG()<<"文件不存在";
    }

    //按照json格式写入数据
    //这个对象就可以当做map一样使用
    QJsonObject jsonObj;
    jsonObj["loginSessionId"] = loginSessionId;

    QJsonObject jsonUnread;
    for(auto it = unreadMessageCount->begin();it != unreadMessageCount->end();++it){
        jsonUnread[it.key()] = it.value();
    }
    jsonObj["unread"] = jsonUnread;

    //把json写入文件
    QJsonDocument jsonDoc(jsonObj);
    QString s = jsonDoc.toJson();
    file.write(s.toUtf8());

    //关闭文件
    file.close();
}

//加载数据文件
void DataCenter::loadDataFile()
{
    //确保在加载之前，先对文件进行初始化操纵
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/ChatClient.json";

    //判定文件是否存在，不存在则初始化，并创建出新的空白的json文件
    QFileInfo fileInfo(filePath);
    if(!fileInfo.exists()){
        initDataFile();
    }

    //以读的方式打开文件
    QFile file(filePath);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        LOG() << "打卡文件失败！" << file.errorString();
        return;
    }

    //读取到文件内容，解析为JSON对象
    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if(jsonDoc.isNull()){
        LOG() << "解析JSON文件失败！JSON文件格式有错误!";
        file.close();
        return;
    }

    QJsonObject jsonObj = jsonDoc.object();
    this->loginSessionId = jsonObj["loginSessionId"].toString();
    LOG() << "loginSessionId=" << this->loginSessionId;

    this->unreadMessageCount->clear();
    QJsonObject jsonUnread = jsonObj["unread"].toObject();
    for(auto it = jsonUnread.begin();it != jsonUnread.end();++it){
        this->unreadMessageCount->insert(it.key(),it.value().toInt());
    }

    file.close();
}

void DataCenter::clearUnread(const QString &chatSessionId)
{
    (*unreadMessageCount)[chatSessionId] = 0;

    //手动保存数据到文件中，防止数据意外丢失
    saveDataFile();
}

void DataCenter::addUnread(const QString &chatSessionId)
{
    ++(*unreadMessageCount)[chatSessionId];

    //手动保存数据到文件中，防止数据意外丢失
    saveDataFile();
}

int DataCenter::getUnread(const QString &chatSessionId)
{
    return (*unreadMessageCount)[chatSessionId];
}

void DataCenter::initWebsocket()
{
    netClient.initWebsocket();
}

void DataCenter::getMyselfAsync()
{
    //DataCenter只是负责"处理数据",真正访问网络进行通信，需要通过NetClient
    netClient.getMyself(loginSessionId);
}

UserInfo *DataCenter::getMyself()
{
    return myself;
}

void DataCenter::resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp)
{
    if(myself == nullptr){
        myself = new UserInfo();
    }

    auto& userInfo = resp->userInfo();

    myself->load(userInfo);
}

void DataCenter::getFriendListAsync()
{
    netClient.getFriendList(loginSessionId);    
}



QList<UserInfo> *DataCenter::getFriendList()
{
    return friendList; 
}

void DataCenter::resetFriendList(std::shared_ptr<bite_im::GetFriendListRsp> resp)
{
    if(friendList == nullptr){
        friendList = new QList<UserInfo>();
    }
    friendList->clear();

    auto& friendListPB = resp->friendList();

    for(auto&f : friendListPB){
        UserInfo userInfo;
        userInfo.load(f);
        friendList->push_back(userInfo);
    }

}

void DataCenter::getChatSessionListAsync()
{
    netClient.getChatSessionList(loginSessionId);
}

QList<ChatSessionInfo> *DataCenter::getChatSessionList()
{
    return chatSessionList;
}

void DataCenter::resetChatSessionList(std::shared_ptr<bite_im::GetChatSessionListRsp> resp)
{
    if(chatSessionList == nullptr){
        chatSessionList = new QList<ChatSessionInfo>();
    }

    chatSessionList->clear();

    auto& chatSessionListPB = resp->chatSessionInfoList();

    for(auto&c : chatSessionListPB){
        ChatSessionInfo chatSessionInfo;
        chatSessionInfo.load(c);
        chatSessionList->push_back(chatSessionInfo);
    }
}

void DataCenter::getApplyListAsync()
{
    netClient.getApplyList(loginSessionId);
}

QList<UserInfo> *DataCenter::getApplyList()
{
    return applyList;
}

void DataCenter::resetApplyList(std::shared_ptr<bite_im::GetPendingFriendEventListRsp> resp)
{
    if(applyList == nullptr){
        applyList = new QList<UserInfo>();
    }

    applyList->clear();

    auto& eventListPB = resp->event();

    for(auto&e : eventListPB){
        UserInfo userInfo;
        userInfo.load(e.sender());
        applyList->push_back(userInfo);
    }
}

void DataCenter::getRecentMessageListAsync(const QString &chatSessionId , bool updateUI)
{
    netClient.getRecentMessageList(loginSessionId,chatSessionId,updateUI);
}

QList<Message> *DataCenter::getRecentMessageList(const QString &chatSessionId)
{
    if(!recentMessages->contains((chatSessionId))){
        return nullptr;
    }
    return &(*recentMessages)[chatSessionId];
}

void DataCenter::resetRecentMessageList(const QString &chatSessionId, std::shared_ptr<bite_im::GetRecentMsgRsp> resp)
{
   //拿到chatSessionId对应的消息类型列表并清空
    QList<model::Message>& messageList = (*recentMessages)[chatSessionId];

    //遍历响应结果的列表
    for(auto& m : resp->msgList()){
        model::Message message;
        message.load(m);

        messageList.push_back(message);
    }

}

void DataCenter::sendTextMessageAsync(const QString &chatSessionId, const QString &content)
{
    netClient.sendMessage(loginSessionId,chatSessionId,MessageType::TEXT_TYPE,content.toUtf8(),"");
}

void DataCenter::sendImageMessageAsync(const QString &chatSessionId, const QByteArray &content)
{
    netClient.sendMessage(loginSessionId,chatSessionId,MessageType::IMAGE_TYPE,content,"");
}

void DataCenter::sendFileMessageAsync(const QString &chatSessionId, const QString &fileName, const QByteArray &content)
{
    netClient.sendMessage(loginSessionId,chatSessionId,MessageType::FILE_TYPE,content,fileName);
}

void DataCenter::sendSpeechMessageAsync(const QString &chatSessionId, const QByteArray &content)
{
    netClient.sendMessage(loginSessionId,chatSessionId,MessageType::SPEECH_TYPE,content,"");
}

void DataCenter::changeNicknameAsync(const QString &nickname)
{
    netClient.changeNickname(loginSessionId,nickname);
}

void DataCenter::resetNickname(const QString &nickname)
{
    if(myself == nullptr){
        LOG() << "[修改昵称]失败! 用户信息不存在!";
        return ;
    }
    myself->nickname = nickname;
}

void DataCenter::changeDescriptionAsync(const QString &desc)
{
    netClient.changeDescription(loginSessionId,desc);
}

void DataCenter::resetDescription(const QString &desc)
{
    if(myself == nullptr){
        LOG() << "[修改签名]失败! 用户信息不存在!";
        return ;
    }
    myself->description = desc;
}

void DataCenter::getVerifyCodeAsync(const QString &phone)
{
    //这个操作，不需要传入loginSessionId
    //后续还需要实现通过手机验证码登录,登录的时候，没有loginSessionId的
    netClient.getVerifyCode(phone);
}

void DataCenter::resetVerifyCodeId(const QString &verifyCodeId)
{
    this->currentVerifyCodeId = verifyCodeId;
}

const QString &DataCenter::getVerifyCodeId()
{
    return currentVerifyCodeId;
}

void DataCenter::changePhoneAsync(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    netClient.changePhone(loginSessionId,phone,verifyCodeId,verifyCode);
}

void DataCenter::resetPhone(const QString &phone)
{
    if(myself == nullptr){
        return;
    }

    myself->phone = phone;
}

void DataCenter::changeAvatarAsync(const QByteArray &imageBytes)
{
    netClient.changeAvatar(loginSessionId,imageBytes);
}

void DataCenter::resetAvatar(const QByteArray &avatar)
{
    if(myself == nullptr){
        return;
    }

    myself->avatar = model::makeIcon(avatar);
}

void DataCenter::deleteFriendAsync(const QString &userId)
{
    netClient.deleteFriend(loginSessionId,userId);
}

void DataCenter::removeFriend(const QString &userId)
{
    //遍历friendList,删除其中匹配的元素即可
    if(friendList == nullptr || chatSessionList == nullptr){
        return ;
    }
    friendList->removeIf([=](const UserInfo& userInfo){
        //返回true要删除的元素,false直接跳过不删除
        return userInfo.userId == userId;
    });

    //还有考虑会话列表
    //删除跟对应好友的会话列表
    //删除会话操作,客户端和服务器分别都会删除
    chatSessionList->removeIf([=](const ChatSessionInfo& chatSessionInfo){
        if(chatSessionInfo.userId == ""){
            //群聊，不受影响
            return false;
        }
        if(chatSessionInfo.userId == userId){
            //当前这个会话要删除了，并且要删除的会话又是选中的会话，才能真正清空当前会话
            //此处如果删除的会话，正好是用户正在选中的会话，此时就需要把当前选中会话(标题跟消息列表)都删除
            if(chatSessionInfo.chatSessionId == this->currentChatSessionId){
                emit this->clearCurrentSession();
            }
            return true;
        }
        return false;
    });

}

void DataCenter::addFriendApplyAsync(const QString &userId)
{
    netClient.addFriendApply(loginSessionId,userId);
}

void DataCenter::acceptFriendApplyAsync(const QString &userId)
{
    netClient.acceptFriendApply(loginSessionId,userId);
}

model::UserInfo DataCenter::removeFromApplyList(const QString &userId)
{
    if(applyList == nullptr){
        return UserInfo();
    }

    for(auto it = applyList->begin();it != applyList->end();++it){
        if(it->userId == userId){
            //复制一下这个要删除的对象,以备进行返回
            model::UserInfo toDelete = *it;
            applyList->erase(it);
            return toDelete;
        }
    }
    return UserInfo();
}

void DataCenter::rejectFriendApplyAsync(const QString &userId)
{
    netClient.rejectFriendApply(loginSessionId,userId);
}

void DataCenter::changeGroupNameAsync(const QString &groupName)
{
    netClient.changeGroupName(loginSessionId,this->getCurrentChatSessionId(),groupName);
}

void DataCenter::resetGroupName(const QString&chatSessionId, const QString &groupName)
{
    model::ChatSessionInfo* chatSessionInfo= this->findChatSessionByChatSessionId(chatSessionId);
    if(chatSessionInfo == nullptr){
        return;
    }
    chatSessionInfo->chatSessionName = groupName;
}

void DataCenter::searchUserAsync(const QString &searchKey)
{
    netClient.searchUser(loginSessionId,searchKey);
}

QList<UserInfo> *DataCenter::getSearchUserResult()
{
    return searchUserResult;
}

void DataCenter::resetSearchUserResult(const QList<bite_im::UserInfo> &userList)
{
    if(searchUserResult == nullptr){
        searchUserResult = new QList<UserInfo>();
    }
    searchUserResult->clear();

    for(const auto& u : userList){
        UserInfo userInfo;
        userInfo.load(u);
        searchUserResult->push_back(userInfo);
    }

}


void DataCenter::searchMessageByKeyAsync(const QString &searchKey)
{
    //搜索的历史消息,根据会话来组织
    netClient.searchMessageByKey(loginSessionId,this->currentChatSessionId,searchKey);

}

void DataCenter::searchMessageByTimeAsync(const QDateTime &begTime, const QDateTime &endTime)
{
    //搜索的历史消息,根据会话来组织
    netClient.searchMessageByTime(loginSessionId,currentChatSessionId,begTime,endTime);
}

QList<Message> *DataCenter::getSearchMessageResult()
{
    return searchMessageResult;
}

void DataCenter::resetSearchMessageResult(const QList<bite_im::MessageInfo> &msgList)
{
    if(this->searchMessageResult == nullptr){
        this->searchMessageResult = new QList<Message>;
    }
    this->searchMessageResult->clear();

    for(const auto& m : msgList){
        Message message;
        message.load(m);
        searchMessageResult->push_back(message);
    }

}

void DataCenter::userLoginAsync(const QString &username, const QString &password)
{
    //登录操作,没有loginSessionId
    //登录成功之后,服务器才会返回loginSessionId
    netClient.userLogin(username,password);
}

void DataCenter::resetLoginSessionId(const QString &loginSessionId)
{
    this->loginSessionId = loginSessionId;

    //一旦会话id改变,就需要保存到硬盘上
    saveDataFile();
}

void DataCenter::userRegisterAsync(const QString &username, const QString &password)
{
    netClient.userRegister(username,password);
}

void DataCenter::phoneLoginAsync(const QString &phone, const QString &verifyCode)
{
    netClient.phoneLogin(phone,this->currentVerifyCodeId,verifyCode);
}

void DataCenter::phoneRegisterAsync(const QString &phone, const QString &verifyCode)
{
    netClient.phoneRegister(phone,this->currentVerifyCodeId,verifyCode);
}

void DataCenter::getSingleFileAsync(const QString &fileId)
{
    netClient.getSingleFile(loginSessionId,fileId);
}

void DataCenter::speechConvertTextAsync(const QString &fileId, const QByteArray &content)
{
    netClient.speechConvertText(loginSessionId,fileId,content);
}


ChatSessionInfo *DataCenter::findChatSessionByChatSessionId(const QString &chatSessionid)
{
    if(chatSessionList == nullptr){
        return nullptr;
    }
    for(auto& info : *chatSessionList){
        if(info.chatSessionId == chatSessionid){
            return &info;
        }
    }
    return nullptr;
}

ChatSessionInfo *DataCenter::findChatSessionByUserId(const QString &userId)
{
    if(chatSessionList == nullptr){
        return nullptr;
    }
    for(auto& info : *chatSessionList){
        if(info.userId == userId){
            return &info;
        }
    }
    return nullptr;
}

void DataCenter::topChatSessionInfo(const ChatSessionInfo &chatSessionInfo)
{
    if(chatSessionList == nullptr){
        return ;
    }

    //1.把这个元素从列表中找到
    auto it = chatSessionList->begin();
    for(;it != chatSessionList->end();++it){
        if(it->chatSessionId == chatSessionInfo.chatSessionId){
            break;
        }
    }

    if(it == chatSessionList->end()){
        //上面的循环没有找到匹配的元素，直接返回，正常来说，不会走这个逻辑
        LOG() << "[错误]未找到匹配元素返回的逻辑有误！";
        return;
    }

    //2.把这个元素备份一下，然后删除
    model::ChatSessionInfo backup = chatSessionInfo;
    chatSessionList->erase(it);

    //3.把备份的元素，插入到头部
    chatSessionList->push_front(backup);
}

UserInfo *DataCenter::findFriendById(const QString &userId)
{
    if(this->friendList == nullptr){
        return nullptr;
    }
    for(auto& f : *friendList){
        if(f.userId == userId){
            return &f;
        }
    }
    return nullptr;
}


void DataCenter::setCurrentChatSessionId(const QString &chatSessionId)
{
    this->currentChatSessionId = chatSessionId;
}

const QString &DataCenter::getCurrentChatSessionId()
{
    return this->currentChatSessionId;
}

void DataCenter::addMessage(const Message &message)
{
    QList<model::Message>& messageList = (*recentMessages)[message.chatSessionId];
    messageList.push_back(message);
}

void DataCenter::createGroupChatSessionAsync(const QList<QString> &userIdList)
{
    netClient.createGroupChatSession(loginSessionId,userIdList);
}

void DataCenter::getMemberListAsync(const QString &chatSessionId)
{
    netClient.getMemberList(loginSessionId,chatSessionId);
}

QList<UserInfo> *DataCenter::getMemberList(const QString &chatSessionId)
{
    if(!this->memberList->contains(chatSessionId)){
        return nullptr;
    }
    return &(*this->memberList)[chatSessionId];
}

void DataCenter::resetMemberList(const QString &chatSessionId, const QList<bite_im::UserInfo> &memberList)
{
    //根据chatSessionId,这个key得到对应的value(QList);
    QList<UserInfo>& currentMemberList = (*this->memberList)[chatSessionId];
    currentMemberList.clear();

    for(const auto& m : memberList){
        UserInfo userInfo;
        userInfo.load(m);
        currentMemberList.push_back(userInfo);
    }
}


}//end namespace
